# routes.pl
use strict;
use warnings;

my $FALSE = 0;
my $TRUE  = 1;

sub routes {
    #
    #   ARRAYREF routes(HASHREF $settings)
    #
    #   Returns a Ruby on Rails inspired url routing pattern definition.
    #
    #   See lib/routing.pl for details of syntax and semantics.
    #
    my ($settings) = @_;

    # set formats to a string representation of a regular expression (e.g. '/json|rdf|terms|xml|yaml/')
    my $ACCEPTED_FORMATS_STR = $settings->{'SUPPORTED_FORMATS_STR'};
    my $ACCEPTED_FORMATS = '/' . $ACCEPTED_FORMATS_STR . '/';
    
    # set default format from either simple content negotiation or literal default extension (e.g. 'xml')
    my $DEFAULT_FORMAT = $settings->{'CONTENT_FORMATS'}{ $ENV{'HTTP_ACCEPT'} || '' } || $settings->{'DEFAULT_FORMAT'};

    # set recognised and default input formats for item creation from text
    my $ACCEPTED_INPUT_FORMATS_STR = 'text|csv|json|xml|arff';
    my $ACCEPTED_INPUT_FORMATS = '/' . $ACCEPTED_INPUT_FORMATS_STR . '/';
    my $DEFAULT_INPUT_FORMAT = 'text';

    # regex string for ids that are also legal perl sub names and legal file names
    my $ACCEPTED_IDS = '/^\p{Ll}(?:\p{Ll}|[\d_])*$/';

    # more loosely constrained id name (e.g. to allow people's names as ids)
    my $ACCEPTED_NAMES = '/^[^\?\\\/\=]+$/';


    my @routes = (

        #
        # DBLP author search utility pages
        #

        [ 'demo/:action.:format',
          {
            controller => 'sift',
            conditions => { method => 'post' },
            format => $ACCEPTED_FORMATS,
            requirements => {
                action => '/search|disambiguate|restrict/',
            },
            defaults => { format => 'html' },
          }
        ],
        [ 'demo/dblp_search_results',
          {
            controller => 'dblp',
            actions => {
                'post'   => 'search_results     names_list refresh',
            },
            requirements => {
                names_list => '/.*/',
            },
            models => {
                'names_list' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>50*1024 },
                'refresh' => { type=>'BOOL' },
            },
            defaults => { format => 'html' },
          }
        ],
        [ 'demo/dblp_search_restrict',
          {
            controller => 'dblp',
            actions => {
                'post'   => 'search_restrict    results_list',
            },
            requirements => {
                results_list => '/.*/',
            },
            models => {
                'results_list' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>100*1024 },
            },
            defaults => { format => 'html' },
          }
        ],
        [ 'demo/dblp_extract',
          {
            controller => 'dblp',
            action => 'extract',
            requirements => {
                uri => '/.*/',
            },
            defaults => { format => 'txt', refresh => 0 },
          }
        ],


        #
        # REST system (inc. workflow and status)
        #

        [ ':user_id/workflow/:workflow_id.:format',
          {
            controller => 'system',
            actions => {
                'delete' => 'workflow_destroy    user_id workflow_id format',
                'head'   => 'workflow_enacting   user_id workflow_id format',
                'post'   => 'workflow_create     user_id workflow_id format commands',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                workflow_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'workflow_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'commands' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>5000*1024 },
            },
          }
        ],
        [ 'status/test/:user_id.:format',
          {
            controller => 'system',
            action => 'status_test',
            requirements => {
                user_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
          }
        ],
        [ 'status/test.:format',
          {
            controller => 'system',
            action => 'status_test',
            format => $ACCEPTED_FORMATS,
            defaults => { format => $DEFAULT_FORMAT, },
          }
        ],


        #
        # REST relations
        #

        [ ':user_id/relations/:folder_id/run.:format',
          {
            controller => 'relations',
            actions => {
                'post'  => 'run             user_id folder_id format',
                'any'   => 'wrong_method    format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
            },
          }
        ],

        [ ':user_id/relations.:format',
          {
            controller => 'relations',
            actions => {
                'get'    => 'list           user_id format full',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'full' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
            },
          }
        ],

        [ ':user_id/relations/:folder_id.:format',
          {
            controller => 'relations',
            actions => {
                'delete' => 'destroy    user_id folder_id format full',
                'get'    => 'show       user_id folder_id format full',
                'head'   => 'exists     user_id folder_id format',
                'post'   => 'create     user_id folder_id format mode description full',
                'put'    => 'update     user_id folder_id format mode description full',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'full' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
            },
          }
        ],


        #
        # REST relation_items
        #

        [ ':user_id/relations/:folder_id/creating.:format',
          {
            controller => 'relation_items',
            actions => {
                'head'  => 'creating        user_id folder_id format',
                'any'   => 'wrong_method    format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/from/*relation_ids.:format',
          {
            controller => 'relations',
            actions => {
                'post'   => 'create_from    user_id folder_id relation_ids format mode description full generator templates',
                'head'   => 'creating_from  user_id folder_id format',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'relation_ids' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>640 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'full' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
                'generator' => { type=>'STR', required=>$FALSE, default=>'map', values=>'map|product|lambda' },
                'templates' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>500*1024 },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/items.:format',
          {
            controller => 'relation_items',
            actions => {
                'post'   => 'create     user_id folder_id format mode description full value from is_schema use_schema id_path description_path',
                'put'    => 'create     user_id folder_id format mode description full value from is_schema use_schema id_path description_path',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
                value => '/.*/',
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'full' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
                'value' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>500000*1024 },
                'from' => { type=>'STR', required=>$FALSE, default=>$DEFAULT_INPUT_FORMAT, values=>$ACCEPTED_INPUT_FORMATS_STR },
                'is_schema' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
                'use_schema' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
                'id_path' => { type=>'STR', required=>$FALSE, default=>'',  maxlength=>64 },
                'description_path' => { type=>'STR', required=>$FALSE, default=>'',  maxlength=>64 },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/items/:item_id.:format',
          {
            controller => 'relation_items',
            actions => {
                'delete' => 'destroy    user_id folder_id item_id format',
                'get'    => 'show       user_id folder_id item_id format',
                'head'   => 'exists     user_id folder_id item_id format',
                'post'   => 'create     user_id folder_id item_id format mode description full value from',
                'put'    => 'update     user_id folder_id item_id format value from',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                item_id => $ACCEPTED_NAMES,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'item_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'full' => { type=>'BOOL', required=>$FALSE, default=>$FALSE },
                'value' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>500000*1024 },
                'from' => { type=>'STR', required=>$FALSE, default=>$DEFAULT_INPUT_FORMAT, values=>$ACCEPTED_INPUT_FORMATS_STR },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/items.:format',
          {
            controller => 'relation_items',
            actions => {
                'get'    => 'list           user_id folder_id format page count',
                'delete' => 'destroy_all    user_id folder_id format',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'page' => { type=>'INT2', required=>$FALSE, default=>1, minvalue=>1 },
                'count' => { type=>'INT2', required=>$FALSE, default=>10, minvalue=>1, maxvalue=>10000 },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/count.:format',
          {
            controller => 'relation_items',
            actions => {
                'get'    => 'count          user_id folder_id format',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
            },
          }
        ],

        [ ':user_id/relations/:folder_id/index.:format',
          {
            controller => 'relation_items',
            actions => {
                'get'    => 'index          user_id folder_id format page count',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'page' => { type=>'INT2', required=>$FALSE, default=>1, minvalue=>1 },
                'count' => { type=>'INT2', required=>$FALSE, default=>10, minvalue=>1, maxvalue=>10000 },
            },
          }
        ],




        #
        # REST reports
        #

        [ ':user_id/reports.:format',
          {
            controller => 'reports',
            actions => {
                'get'    => 'list           user_id format',
                'any'    => 'wrong_method   format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
            },
          }
        ],

        [ ':user_id/reports/:folder_id/profiles/:profiles_id.:format',
          {
            controller => 'reports',
            actions => {
                'post'   => 'create_profiles    user_id folder_id format mode description type profiles_id',
                'put'    => 'update             user_id folder_id format mode description type profiles_id',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { profiles_id => '', format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'type' => { type=>'STR', required=>$FALSE, default=>'html', values=>'html|graphviz' },
                'profiles_id' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>64 },
            },
          }
        ],
        
        [ ':user_id/reports/:folder_id/matches/:matches_id.:format',
          {
            controller => 'reports',
            actions => {
                'post'   => 'create_matches     user_id folder_id format mode description type matches_id strength',
                'put'    => 'update             user_id folder_id format mode description type matches_id',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { matches_id => '', format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
                'type' => { type=>'STR', required=>$FALSE, default=>'html', values=>'html|graphviz' },
                'matches_id' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>64 },
                'strength' => { type=>'NUM', required=>$FALSE, default=>95, minvalue=>1, maxvalue=>100 },
            },
          }
        ],
        
        # serve pre-generated static files from reports folder under folder's access control
        [ ':user_id/reports/:folder_id/*file.:format',
          {
            controller => 'reports',
            actions => {
                'get'    => 'view       user_id folder_id file format',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => '/html|zip|png|pdf|jpg|svg|dot/',     #lets xml,json,etc. non-statics be handled by a later route below
            },
            defaults => { format => 'html', },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'file' => { type=>'STR', required=>$FALSE, default=>'index', maxlength=>250 },
                'format' => { type=>'STR', required=>$FALSE, default=>'html', values=>'html|zip|png|pdf|jpg|svg|dot' },
            },
          }
        ],

        [ ':user_id/reports/:folder_id.:format',
          {
            controller => 'reports',
            actions => {
                'delete' => 'destroy    user_id folder_id format',
                'get'    => 'show       user_id folder_id format',
                'head'   => 'exists     user_id folder_id format',
#                'post'   => 'create     user_id folder_id format mode description',
#                'put'    => 'update     user_id folder_id format mode description',
            },
            requirements => {
                user_id => $ACCEPTED_IDS,
                folder_id => $ACCEPTED_IDS,
                format => $ACCEPTED_FORMATS,
            },
            defaults => { format => $DEFAULT_FORMAT, },
            models => {
                'user_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'folder_id' => { type=>'STR', required=>$TRUE, minlength=>1, maxlength=>64 },
                'format' => { type=>'STR', default=>$DEFAULT_FORMAT, values=>$ACCEPTED_FORMATS_STR },
                'mode' => { type=>'STR', required=>$FALSE, default=>'public', values=>'public|private' },
                'description' => { type=>'STR', required=>$FALSE, default=>'', maxlength=>1024 },
            },
          }
        ],


        #
        # Default routes
        #

        [ ':controller/:action',
          {
            controller => 'default',
            defaults => { action => 'index', },
          }
        ],

        # Catch-all
        # NB. will only match if no physical index.html (or similar) file
        [ '/',
          {
            controller => 'default',
            action => 'index',
          }
        ],

    # Example usage:
    #  [ ':controller/:action/:id.:format',
    #    {
    #      conditions => { method => 'any' },
    #      controller => '/users|accounts|groups/',
    #      language => 'en_uk',
    #      requirements => {
    #           action => $ACCEPTED_IDS,
    #           page => '/^[\d]{1,3}/',
    #           id => $ACCEPTED_IDS,
    #      },
    #      defaults => { action => 'index', id => 'nil', },
    #    }
    #  ],
    );
    return \@routes;
}

1;